package com.wjk.kylin.id.server.uid.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wjk.kylin.id.server.dto.QueryWorkerIdInDTO;
import com.wjk.kylin.id.server.dto.WorkerIdDTO;
import com.wjk.kylin.id.server.exception.BaseError;
import com.wjk.kylin.id.server.exception.BusinessException;
import com.wjk.kylin.id.server.transactional.TransactionalHandle;
import com.wjk.kylin.id.server.uid.entity.UIdInfoEntity;
import com.wjk.kylin.id.server.uid.mapper.UIdInfoMapper;
import com.wjk.kylin.id.server.uid.service.IUIdInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * uid信息表 服务实现类
 * </p>
 *
 * @author wangjinkui
 * @since 2022-09-22
 */
@Slf4j
@Service
public class UIdInfoServiceImpl extends ServiceImpl<UIdInfoMapper, UIdInfoEntity> implements IUIdInfoService {
    @Autowired
    private TransactionalHandle transactionalHandle;


    /**
     * 百度uid 查询工作id
     *
     * @param inDTO
     * @return
     */
    @Override
    public WorkerIdDTO queryWorkerId(QueryWorkerIdInDTO inDTO) {
        //需要预制一条数据
        //每次都是自增 worker id
        UIdInfoEntity idInfoEntity = getUIdInfoEntity(inDTO);

        //调用执行事务
        final UIdInfoEntity finalIdInfoEntity = idInfoEntity;
        //第一次
        boolean update = transactionalHandle.execute(() -> updateIdInfoEntity(finalIdInfoEntity));

        //验证 update ,进行再次更新，直到为true
        idInfoEntity = this.getIdInfoEntityAndUpdate(update, idInfoEntity.getId(), inDTO, idInfoEntity);

        //组织返回数据
        return WorkerIdDTO.builder()
                //更新后的worker id
                .workerId(idInfoEntity.getWorkerId() + 1)
                .build();
    }

    /**
     * 验证 update ,进行再次更新，直到为true
     *
     * @param update
     * @param id
     * @param inDTO
     * @param idInfoEntity
     */
    private UIdInfoEntity getIdInfoEntityAndUpdate(boolean update, Long id, QueryWorkerIdInDTO inDTO, UIdInfoEntity idInfoEntity) {
        int count = 0;
        while (!update) {
            idInfoEntity = super.getById(id);
            if (idInfoEntity == null) {
                log.error("kylin_uid_info 数据为空,bizType:{},id:{}", inDTO.getBizType(), id);
                throw new BusinessException(BaseError.BIZ_TYPE_PARAM_ERR);
            }

            final UIdInfoEntity finalIdInfoEntity = idInfoEntity;
            update = transactionalHandle.execute(() -> updateIdInfoEntity(finalIdInfoEntity));

            log.warn("update kylin_uid_info,bizType:{},id:{},count:{},update:{}", inDTO.getBizType(), id, ++count, update);
        }

        return idInfoEntity;
    }


    /**
     * 执行事务方法
     *
     * @param idInfoEntity
     * @return
     */
    private boolean updateIdInfoEntity(UIdInfoEntity idInfoEntity) {
        //更新的数据
        UIdInfoEntity updateIdInfoEntity = new UIdInfoEntity();
        updateIdInfoEntity.setWorkerId(idInfoEntity.getWorkerId() + 1);
        updateIdInfoEntity.setVersion(idInfoEntity.getVersion() + 1);

        //根据id 和 version 更新数据
        LambdaUpdateWrapper<UIdInfoEntity> infoUpdateWrapper = new LambdaUpdateWrapper<>();
        infoUpdateWrapper.eq(UIdInfoEntity::getId, idInfoEntity.getId());
        infoUpdateWrapper.eq(UIdInfoEntity::getVersion, idInfoEntity.getVersion());

        return this.update(updateIdInfoEntity, infoUpdateWrapper);
    }


    /**
     * 查询 info 表数据
     *
     * @param inDTO
     * @return
     */
    private UIdInfoEntity getUIdInfoEntity(QueryWorkerIdInDTO inDTO) {
        LambdaQueryWrapper<UIdInfoEntity> infoQueryWrapper = new LambdaQueryWrapper<>();
        infoQueryWrapper.eq(StringUtils.isNotBlank(inDTO.getBizType()), UIdInfoEntity::getBizType, inDTO.getBizType());

        List<UIdInfoEntity> list = this.list(infoQueryWrapper);

        if (CollectionUtils.isEmpty(list)) {
            log.error("kylin_uid_info 数据为空,bizType:{}", inDTO.getBizType());
            throw new BusinessException(BaseError.BIZ_TYPE_PARAM_ERR);
        } else if (list.size() > 1) {
            log.error("kylin_uid_info 数据大于1,bizType:{},count:{}", inDTO.getBizType(), list.size());
            throw new BusinessException(BaseError.BIZ_TYPE_PARAM_ERR);
        }

        return list.get(0);
    }
}
